#region MIT License
/*
 *  Copyright (c) 2010 Nathan Palmer
 *  http://www.nathanpalmer.com/
 * 
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 * 
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 * 
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
#endregion

using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using MetaSource.Command;
using NLog;
using Quartz;
using SimpleScheduler.Core.Common;
using System.Text;

namespace SimpleScheduler.Job
{
    public class CommandLineJob : IStatefulJob, IInterruptableJob
    {
        private Logger log;
        private bool interrupted = false;

        public CommandLineJob()
        {
            log = LogManager.GetLogger(typeof(CommandLineJob).ToString());
        }

        public void Execute(JobExecutionContext context)
        {
            interrupted = false;

            try
            {
                String name = context.Get("Name") as String;
                String command = context.Get("Command") as String;
                String parameters = context.Get("Arguments") as String;
                Boolean shellExecute = !Convert.ToBoolean(context.Get("Hidden"));
                String workingDirectory = context.Get("WorkingDirectory") as String ?? "";
                int timeout = Convert.ToInt32(context.Get("Timeout"));

                FileInfo commandInfo = new FileInfo(command);

                ShellWrapper sw = new ShellWrapper(commandInfo.DirectoryName, commandInfo.FullName);
                sw.WorkingDirectory = workingDirectory;
                sw.UseShellExecute = shellExecute;

                Stopwatch clock = Stopwatch.StartNew();
                sw.ExecuteCommandAsync(parameters);

                while (sw.IsExecuting && (timeout == 0 || clock.Elapsed.TotalMinutes < timeout))
                {
                    if (interrupted)
                    {
                        sw.Kill();
                        clock.Stop();
                        context.JobDetail.JobDataMap["TaskResult"] = "Interrupted";
                        context.JobDetail.JobDataMap["TaskOutput"] = "";
                        return;
                    }

                    Thread.Sleep(1000);
                }

                if (sw.IsExecuting)
                {
                    log.Log(context.PrepareMessage(NLog.LogLevel.Error, string.Format("The task {0} was still running after {1} minutes and was terminated", name, timeout)));
                    sw.Kill();
                    context.JobDetail.JobDataMap["TaskResult"] = "Interrupted";
                    context.JobDetail.JobDataMap["TaskOutput"] = "";
                }
                else
                {
                    context.JobDetail.JobDataMap["TaskResult"] = string.Format("{0} ({1})", sw.ReturnValue.Status.ToString("g"), sw.ReturnValue.ExitCode);
                    if (sw.ReturnValue.OutputText.Length > 1024)
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.AppendLine(sw.ReturnValue.OutputText.Substring(0, 1024));
                        sb.AppendLine("...");
                        sb.AppendLine(sw.ReturnValue.OutputText.Substring(sw.ReturnValue.OutputText.Length - 1024, 1024));

                        context.JobDetail.JobDataMap["TaskOutput"] = sb.ToString();
                    }
                    else
                    {
                        context.JobDetail.JobDataMap["TaskOutput"] = sw.ReturnValue.OutputText;
                    }                    
                }

                clock.Stop();
            }
            catch(Exception ex)
            {
                context.JobDetail.JobDataMap["TaskResult"] = "Exception";
                context.JobDetail.JobDataMap["TaskOutput"] = ex.ToString();
            }
        }

        public void Interrupt()
        {
            interrupted = true;
        }
    }
}